1 module d_tree_sitter.node; 2 3 extern (C): 4 5 import d_tree_sitter.language; 6 import d_tree_sitter.tree_cursor; 7 import d_tree_sitter.tree_visitor; 8 import d_tree_sitter.other; 9 10 import std : iota, Nullable; 11 import std..string : fromStringz, toStringz; 12 13 /** A single `Node` within a syntax `Tree`. */ 14 struct Node 15 { 16 import d_tree_sitter.libc : TSNode, ts_node_symbol, ts_node_type, ts_tree_language, 17 ts_node_is_named, ts_node_is_extra, ts_node_has_changes, ts_node_has_error, 18 ts_node_is_missing, ts_node_start_byte, ts_node_end_byte, 19 ts_node_start_point, ts_node_end_point, ts_node_child, ts_node_child_count, 20 ts_node_named_child, ts_node_named_child_count, ts_node_child_by_field_name, 21 ts_node_child_by_field_id, ts_node_parent, ts_node_next_sibling, ts_node_prev_sibling, 22 ts_node_next_named_sibling, 23 ts_node_prev_named_sibling, 24 ts_node_descendant_for_byte_range, 25 ts_node_named_descendant_for_byte_range, 26 ts_node_descendant_for_point_range, ts_node_named_descendant_for_point_range, 27 ts_node_string, ts_tree_cursor_new, ts_node_edit, ts_node_is_null; 28 29 /** The internal `TSNode` */ 30 TSNode tsnode; 31 32 /** Create a new Node. 33 Throws: 34 If the passed tsnode is null, it will trigger an error in the debug mode. 35 */ 36 this(TSNode tsnode) @nogc nothrow 37 { 38 debug 39 { 40 assert(!ts_node_is_null(tsnode), "The given tsnode is null"); 41 } 42 this.tsnode = tsnode; 43 } 44 45 /** Creates a new Node from the given nullable TSNode 46 47 Params: 48 tsnode = a C tsnode, which can be a `null` node. 49 50 Returns: 51 a `Nullable!Node`, which gives the node if it is not a `null` node, and `null` if it is. 52 */ 53 static Nullable!Node create(TSNode tsnode) @trusted @nogc nothrow 54 { 55 if (!ts_node_is_null(tsnode)) 56 { 57 return Nullable!Node(Node(tsnode)); 58 } 59 else 60 { 61 return Nullable!Node.init; 62 } 63 } 64 65 /** Check if the Node is a `null` node. 66 67 Note: 68 this function should be only used when creating a `Node` with its constructor in the release mode (instead of using `Node.create`). 69 All the methods of `Node` already use `Node.create` and return a `Nullable!Node`. 70 */ 71 bool isNull() 72 { 73 return ts_node_is_null(tsnode); 74 } 75 76 /** 77 Get a numeric id for this node that is unique. 78 79 Within a given syntax tree, no two nodes have the same id. However, if 80 a new tree is created based on an older tree, and a node from the old 81 tree is reused in the process, then that node will have the same id in 82 both trees. 83 */ 84 auto id() const @nogc nothrow 85 { 86 return tsnode.id; 87 } 88 89 /** Get this node's type as a numerical id. */ 90 auto kind_id() const @nogc nothrow 91 { 92 return ts_node_symbol(tsnode); 93 } 94 95 /** Get this node's type as a string. */ 96 auto kind() const @nogc nothrow 97 { 98 return fromStringz(ts_node_type(tsnode)); 99 } 100 101 /** Get the [Language] that was used to parse this node's syntax tree. */ 102 auto language() const @nogc nothrow 103 { 104 return Language(ts_tree_language(tsnode.tree)); 105 } 106 107 /** 108 Check if this node is *named*. 109 110 Named nodes correspond to named rules in the grammar, whereas *anonymous* nodes 111 correspond to string literals in the grammar. 112 */ 113 auto is_named() const @nogc nothrow 114 { 115 return ts_node_is_named(tsnode); 116 } 117 118 /** 119 Check if this node is *extra*. 120 121 Extra nodes represent things like comments, which are not required the grammar, 122 but can appear anywhere. 123 */ 124 auto is_extra() const @nogc nothrow 125 { 126 return ts_node_is_extra(tsnode); 127 } 128 129 /** 130 Check if this node has been edited 131 */ 132 auto has_changes() const @nogc nothrow 133 { 134 return ts_node_has_changes(tsnode); 135 } 136 137 /** 138 Check if this node represents a syntax error or contains any syntax errors anywhere 139 within it. 140 */ 141 auto has_error() const @nogc nothrow 142 { 143 return ts_node_has_error(tsnode); 144 } 145 146 /** 147 Check if this node represents a syntax error. 148 149 Syntax errors represent parts of the code that could not be incorporated into a 150 valid syntax tree. 151 */ 152 auto is_error() const @nogc nothrow 153 { 154 return kind_id() == ushort.max; 155 } 156 157 /** 158 Check if this node is *missing*. 159 160 Missing nodes are inserted by the parser in order to recover from certain kinds of 161 syntax errors. 162 */ 163 auto is_missing() const @nogc nothrow 164 { 165 return ts_node_is_missing(tsnode); 166 } 167 168 /** 169 Get the byte offsets where this node starts 170 */ 171 auto start_byte() const @nogc nothrow 172 { 173 return ts_node_start_byte(tsnode); 174 } 175 176 /** 177 Get the byte offsets where this node end. 178 */ 179 auto end_byte() const @nogc nothrow 180 { 181 return ts_node_end_byte(tsnode); 182 } 183 184 /** 185 Get the byte range of source code that this node represents. 186 */ 187 auto byte_range() const @nogc nothrow 188 { 189 return iota(start_byte(), end_byte()); 190 } 191 192 /** 193 Get this node's start position in terms of rows and columns. 194 */ 195 auto start_position() const @nogc nothrow 196 { 197 return ts_node_start_point(tsnode); 198 } 199 200 /** 201 Get this node's end position in terms of rows and columns. 202 */ 203 auto end_position() const @nogc nothrow 204 { 205 return ts_node_end_point(tsnode); 206 } 207 208 /** 209 Get the range of source code that this node represents, both in terms of raw bytes 210 and of row/column coordinates. 211 */ 212 auto range() const @nogc nothrow 213 { 214 return Range(start_position(), end_position(), start_byte(), end_byte()); 215 } 216 217 /** 218 Get the node's child at the given index, where zero represents the first 219 child. 220 221 This method is fairly fast, but its cost is technically log(child_index), so you 222 if you might be iterating over a long list of children, you should use 223 [Node::children] instead. 224 225 Returns: 226 A `Nulllable!Node` 227 */ 228 auto child(size_t child_index) const @nogc nothrow 229 { 230 return Node.create(ts_node_child(tsnode, cast(uint)(child_index))); 231 } 232 233 /** 234 Get this node's number of children 235 */ 236 auto child_count() const @nogc nothrow 237 { 238 return ts_node_child_count(tsnode); 239 } 240 241 /** 242 Get this node's *named* child at the given index. 243 244 See also [Node::is_named]. 245 This method is fairly fast, but its cost is technically log(i), so you 246 if you might be iterating over a long list of children, you should use 247 [Node::named_children] instead. 248 249 Returns: 250 A `Nulllable!Node` 251 */ 252 auto named_child(size_t i) const @nogc nothrow 253 { 254 return Node.create(ts_node_named_child(tsnode, cast(uint)(i))); 255 } 256 257 /** 258 Get this node's number of *named* children. 259 260 See also [Node::is_named]. 261 */ 262 auto named_child_count() const @nogc nothrow 263 { 264 return ts_node_named_child_count(tsnode); 265 } 266 267 /** 268 Get the first child with the given field name. 269 270 If multiple children may have the same field name, access them using 271 [children_by_field_name](Node::children_by_field_name) 272 273 Returns: 274 A `Nulllable!Node` 275 */ 276 auto child_by_field_name(string field_name) const 277 { 278 // convert to c string 279 auto field_name_c = toStringz(field_name); 280 auto field_name_length = cast(uint)(field_name.length); 281 return Node.create(ts_node_child_by_field_name(tsnode, field_name_c, field_name_length)); 282 } 283 284 /** 285 Get the first child with the given field name. 286 287 If multiple children may have the same field name, access them using 288 [children_by_field_name](Node::children_by_field_name) 289 290 Returns: 291 A `Nulllable!Node` 292 */ 293 auto child_by_field_id(ushort field_id) const @nogc nothrow 294 { 295 return Node.create(ts_node_child_by_field_id(tsnode, field_id)); 296 } 297 298 /** Iterate over this node children. 299 300 A [TreeCursor] is used to retrieve the children efficiently. Obtain 301 a [TreeCursor] by calling [Tree::walk] or [Node::walk]. To avoid unnecessary 302 allocations, you should reuse the same cursor for subsequent calls to 303 this method. 304 305 If you're walking the tree recursively, you may want to use the `TreeCursor` 306 APIs directly instead. 307 */ 308 auto children(TreeCursor* cursor) const @nogc nothrow 309 { 310 return NodeChildren(this, cursor); 311 } 312 313 /** Iterate over this node named children. 314 315 See also [Node::children]. 316 */ 317 auto named_children(TreeCursor* cursor) const @nogc nothrow 318 { 319 return NodeNamedChildren(this, cursor); 320 } 321 322 /** Iterate over this node children with a given field id. 323 324 See also [Node::children_by_field_name]. 325 */ 326 auto children_by_field_id(ushort field_id, TreeCursor* cursor) const @nogc nothrow 327 { 328 return NodeChildrenByFieldID(this, field_id, cursor); 329 } 330 331 /** Iterate over this node children with a given field name. 332 333 See also [Node::children]. 334 */ 335 auto children_by_field_name(string field_name, TreeCursor* cursor) const 336 { 337 auto field_id = language().field_id_for_name(field_name); 338 return children_by_field_id(field_id, cursor); 339 } 340 341 /** Check if the node has a immediate parent 342 Note: 343 `parent` method already does this check 344 */ 345 auto has_parent() const @nogc nothrow 346 { 347 auto maybe_parent = ts_node_parent(tsnode); 348 return !ts_node_is_null(maybe_parent); 349 } 350 351 /** Get this node immediate parent. 352 353 Returns: 354 a `Nullable!Node`, which gives the parent node if it has a parent, and `null` if the node has no parent. 355 */ 356 auto parent() const @nogc nothrow 357 { 358 return Node.create(ts_node_parent(tsnode)); 359 } 360 361 /** Find the nth parent of node. It goes up until it hits a null parent or `max_nth`. 362 Params: 363 node = the node 364 max_nth = the maximum level to go up. 365 Returns: 366 A node. If the given node doesn't have a parent, it returns the node itself. 367 Note: the nth might not be reached if there are no more parents. 368 */ 369 auto nth_parent(in uint max_nth = 2) @nogc nothrow @trusted const 370 { 371 auto maybeFirstParent = this.parent(); 372 if (maybeFirstParent.isNull()) 373 { 374 // return the node itself 375 return this; 376 } 377 auto parent = maybeFirstParent.get(); 378 379 uint nth = 1; 380 while (nth != max_nth) 381 { 382 auto maybe_parent = parent.parent(); 383 if (!maybe_parent.isNull()) 384 { 385 parent = maybe_parent.get(); 386 } 387 else 388 { 389 break; 390 } 391 ++nth; 392 } 393 return parent; 394 } 395 396 /** Check if the node has a next sibling. 397 Note: 398 `next_sibling` method already does this check 399 */ 400 auto has_next_sibling() const @nogc nothrow 401 { 402 auto maybeNode = ts_node_next_sibling(tsnode); 403 return !ts_node_is_null(maybeNode); 404 } 405 406 /** Get this node next sibling. 407 Returns: 408 A `Nulllable!Node` 409 */ 410 auto next_sibling() const @nogc nothrow 411 { 412 return Node.create(ts_node_next_sibling(tsnode)); 413 } 414 415 /** Check if the node has a previous sibling. 416 Note: 417 `prev_sibling` method already does this check 418 */ 419 auto has_prev_sibling() const @nogc nothrow 420 { 421 auto maybeNode = ts_node_prev_sibling(tsnode); 422 return !ts_node_is_null(maybeNode); 423 } 424 425 /** Get this node previous sibling. 426 427 Returns: 428 A `Nulllable!Node` 429 */ 430 auto prev_sibling() const @nogc nothrow 431 { 432 return Node.create(ts_node_prev_sibling(tsnode)); 433 } 434 435 /** Get this node next named sibling. 436 437 Returns: 438 A `Nulllable!Node` 439 */ 440 auto next_named_sibling() const @nogc nothrow 441 { 442 return Node.create(ts_node_next_named_sibling(tsnode)); 443 } 444 445 /** Get this node previous named sibling. 446 447 Returns: 448 A `Nulllable!Node` 449 */ 450 auto prev_named_sibling() const @nogc nothrow 451 { 452 return Node.create(ts_node_prev_named_sibling(tsnode)); 453 } 454 455 /** Get the smallest node within this node that spans the given range. 456 457 Returns: 458 A `Nulllable!Node` 459 */ 460 auto descendant_for_byte_range(uint start, uint end) const @nogc nothrow 461 { 462 return Node.create(ts_node_descendant_for_byte_range(tsnode, start, end)); 463 } 464 465 /** Get the smallest named node within this node that spans the given range. 466 467 Returns: 468 A `Nulllable!Node` 469 */ 470 auto named_descendant_for_byte_range(uint start, uint end) const @nogc nothrow 471 { 472 return Node.create(ts_node_named_descendant_for_byte_range(tsnode, start, end)); 473 } 474 475 /** Get the smallest node within this node that spans the given range. 476 477 Returns: 478 A `Nulllable!Node` 479 */ 480 auto descendant_for_point_range(Point start, Point end) const @nogc nothrow 481 { 482 return Node.create(ts_node_descendant_for_point_range(tsnode, start, end)); 483 } 484 485 /** Get the smallest named node within this node that spans the given range. 486 487 Returns: 488 A `Nulllable!Node` 489 */ 490 auto named_descendant_for_point_range(Point start, Point end) const @nogc nothrow 491 { 492 return Node.create(ts_node_named_descendant_for_point_range(tsnode, start, end)); 493 } 494 495 /** Convert Node to string */ 496 auto to_string() const nothrow 497 { 498 import core.memory : pureFree; 499 500 auto c_string = ts_node_string(tsnode); // NOTE requires freeing the heap allocated string 501 string str = fromStringz(c_string).dup; // TODO use automem instead of copying and freeing the original? 502 pureFree(c_string); 503 return str; 504 } 505 506 alias to_sexp = to_string; 507 508 import std..string : assumeUTF; 509 510 /** Convert Node to utf8 string */ 511 auto utf8_text(string source_code) const @nogc nothrow 512 { 513 import std : representation; 514 515 const source = source_code.representation(); 516 return assumeUTF(source[start_byte() .. end_byte()]); 517 } 518 519 /// ditto 520 auto utf8_text(ubyte[] source) const @nogc nothrow 521 { 522 return assumeUTF(source[start_byte() .. end_byte()]); 523 } 524 525 /** Convert Node to utf16 string */ 526 auto utf16_text(ushort[] source) const @nogc nothrow 527 { 528 return assumeUTF(source[start_byte() .. end_byte()]); 529 } 530 531 /** Create a new [TreeCursor] starting from this node. */ 532 auto walk() const @nogc nothrow 533 { 534 return TreeCursor(ts_tree_cursor_new(tsnode)); 535 } 536 537 /** Hash a node. This returns a unique string for this node. */ 538 auto hash() @trusted const @nogc 539 { 540 import bc..string : nogcFormat; 541 542 return nogcFormat!"%d%d"(this.kind_id(), this.start_byte()); 543 } 544 545 /** 546 Traverse this `Node` and all its descendants in a top-down left to right manner while 547 applying the visitor at each [Node]. 548 */ 549 void traverse(TreeVisitor visitor) const 550 { 551 auto cursor = walk(); 552 visitor.enter_node(&cursor); 553 auto recurse = true; 554 while (true) 555 { 556 if (recurse && cursor.goto_first_child()) 557 { 558 recurse = visitor.enter_node(&cursor); 559 } 560 else 561 { 562 visitor.leave_node(&cursor); 563 if (cursor.goto_next_sibling()) 564 { 565 recurse = visitor.enter_node(&cursor); 566 } 567 else if (cursor.goto_parent()) 568 { 569 recurse = false; 570 } 571 else 572 { 573 break; 574 } 575 } 576 } 577 } 578 579 /** 580 Traverse this `Node` and all its descendants in a top-down left to right manner while 581 applying the visitor at each [Node]. 582 583 NOTE: if you are sure that TreeVisitor is nothrow, you can use this method 584 */ 585 void traverse_nothrow(TreeVisitor visitor) const nothrow 586 { 587 import std : assumeWontThrow; 588 589 assumeWontThrow(traverse(visitor)); 590 } 591 592 /** Edit this node to keep it in-sync with source code that has been edited. 593 594 This function is only rarely needed. When you edit a syntax tree with the 595 [Tree::edit] method, all of the nodes that you retrieve from the tree 596 afterward will already reflect the edit. You only need to use [Node::edit] 597 when you have a specific [Node] instance that you want to keep and continue 598 to use after an edit. 599 */ 600 auto edit(const InputEdit* edit) @nogc nothrow 601 { 602 return ts_node_edit(&tsnode, edit); 603 } 604 } 605 606 /** A range to iterate over the node children. 607 608 A [TreeCursor] is used to retrieve the children efficiently. Obtain 609 a [TreeCursor] by calling [Tree::walk] or [Node::walk]. To avoid unnecessary 610 allocations, you should reuse the same cursor for subsequent calls to 611 this method. 612 613 If you're walking the tree recursively, you may want to use the `TreeCursor` 614 APIs directly instead. 615 */ 616 struct NodeChildren 617 { 618 private TreeCursor* cursor; 619 620 private const uint count; 621 private uint iChild = 0; 622 623 /** create a NodeChildren for the given node and cursor */ 624 auto this(Node parent, TreeCursor* cursor) @nogc nothrow 625 { 626 this.cursor = cursor; 627 628 cursor.reset(parent); 629 cursor.goto_first_child(); 630 this.count = parent.child_count(); 631 } 632 633 /** Get the current child */ 634 auto front() const @nogc nothrow 635 { 636 return cursor.node(); 637 } 638 639 /** go to the next child */ 640 void popFront() @nogc nothrow 641 { 642 cursor.goto_next_sibling(); 643 iChild++; 644 } 645 646 /** Is it the end? */ 647 auto empty() const @nogc nothrow 648 { 649 return iChild == count; 650 } 651 } 652 653 /** A range the iterates over the node named children. 654 655 See also [Node::children]. 656 */ 657 struct NodeNamedChildren 658 { 659 private TreeCursor* cursor; 660 661 private const uint count; 662 private uint iChild = 0; 663 664 /** create a NodeNamedChildren for the given node and cursor */ 665 auto this(Node parent, TreeCursor* cursor) @nogc nothrow 666 { 667 this.cursor = cursor; 668 669 cursor.reset(parent); 670 cursor.goto_first_child(); 671 this.count = parent.named_child_count(); 672 } 673 674 /** Finds the front node */ 675 private void preFront() @nogc nothrow 676 { 677 while (!cursor.node().is_named()) 678 { 679 if (!cursor.goto_next_sibling()) 680 { 681 break; 682 } 683 } 684 } 685 686 /** 687 Get the current child 688 NOTE Do not call this twice in a row without calling popFront and empty in between! 689 */ 690 auto front() @nogc nothrow 691 { 692 preFront(); 693 return cursor.node(); 694 } 695 696 /** go to the next child */ 697 void popFront() @nogc nothrow 698 { 699 cursor.goto_next_sibling(); 700 iChild++; 701 } 702 703 /** Is it the end? */ 704 auto empty() const @nogc nothrow 705 { 706 return iChild == count; 707 } 708 } 709 710 /** A range to iterate over the node children with a given field id. 711 712 See also [Node::children_by_field_name]. 713 */ 714 struct NodeChildrenByFieldID 715 { 716 private TreeCursor* cursor; 717 private ushort field_id; 718 719 private bool noMoreNextSibiling = false; 720 721 /** create a NodeChildrenByFieldID for the given node, field_id, and cursor */ 722 auto this(Node parent, ushort field_id, TreeCursor* cursor) @nogc nothrow 723 { 724 this.cursor = cursor; 725 this.field_id = field_id; 726 727 cursor.reset(parent); 728 cursor.goto_first_child(); 729 } 730 731 /** Finds the front node */ 732 private void preFront() @nogc nothrow 733 { 734 // find the node related to field_id 735 while (cursor.field_id() != field_id) 736 { 737 popFront(); 738 } 739 } 740 741 /** 742 Get the current child 743 NOTE Do not call this twice in a row without calling popFront and empty in between! 744 */ 745 auto front() @nogc nothrow 746 { 747 preFront(); 748 return cursor.node(); 749 } 750 751 /** go to the next child */ 752 void popFront() @nogc nothrow 753 { 754 // check if this found field_id is the last one 755 if (!cursor.goto_next_sibling()) 756 { 757 // if no next sibiling then we are done finding 758 noMoreNextSibiling = true; 759 } 760 } 761 762 /** Is it the end? */ 763 auto empty() const @nogc nothrow 764 { 765 return noMoreNextSibiling; 766 } 767 }